package com.alks.function.service.impl;

import com.alks.common.data.dto.SysUserTokenDTO;
import com.alks.common.data.thread.UserIdThread;
import com.alks.common.service.RedisService;
import com.alks.common.utils.JWTUtil;
import com.alks.common.utils.MD5Utils;
import com.alks.common.utils.beanutils.ArrayUtils;
import com.alks.common.utils.beanutils.BeanUtil;
import com.alks.common.utils.stringUtils.ZStringUtils;
import com.alks.entity.data.entity.SdPrgPrivilege;
import com.alks.entity.data.entity.SdProgram;
import com.alks.entity.data.entity.SdUser;
import com.alks.entity.data.enums.ResultCodeEnum;
import com.alks.common.data.enums.system.LoginPermEnum;
import com.alks.entity.data.response.ResponseInfo;
import com.alks.entity.exception.ServiceErrorException;
import com.alks.function.data.dto.sys.SdUserDto;
import com.alks.function.data.request.sys.SysUserRequest;
import com.alks.function.mapper.SdUserMapper;
import com.alks.function.mapper.system.SYS_SdPrgPrivilegeMapper;
import com.alks.function.mapper.system.SYS_SdProgramMapper;
import com.alks.function.service.SysUserService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.*;
import java.util.stream.Collectors;

import static com.alks.entity.data.enums.ResultCodeEnum.LOGIN_ERROR;

@Slf4j
@Service
public class SysUserServiceImpl extends ServiceImpl<SdUserMapper, SdUser> implements SysUserService {
    @Autowired
    private RedisService redisService;
    @Autowired
    private SYS_SdProgramMapper programMapper;
    @Autowired
    private SYS_SdPrgPrivilegeMapper privilegeMapper;
    @Resource
    private SdUserMapper sdUserMapper;


    static final Set<String> superName = new HashSet<>();

    static {
        superName.add("林添发");
        superName.add("林添发1");
    }


    @Override
    public ResponseInfo login(SysUserRequest sysUser, HttpServletRequest servletRequest) {
        if (sysUser == null) {
            return ResponseInfo.error(ResultCodeEnum.LOGIN_CONFIG_IS_NULL.getMessage(), ResultCodeEnum.LOGIN_CONFIG_IS_NULL.getCode());
        }
        if (BeanUtil.allIsEmpty(sysUser.getPassword()) || BeanUtil.allIsEmpty(sysUser.getLoginName())) {
            return ResponseInfo.error(ResultCodeEnum.LOGIN_CONFIG_IS_NULL.getMessage(), ResultCodeEnum.LOGIN_CONFIG_IS_NULL.getCode());
        }
        String username = sysUser.getLoginName().toUpperCase();
        SdUser baseSysUser = lambdaQuery().eq(SdUser::getEngName, username).one();

        //根据用户名获取用户数据
        if (baseSysUser == null) {
            return ResponseInfo.error(ResultCodeEnum.LOGIN_USERNAME_NOT_EXIST.getMessage(), ResultCodeEnum.LOGIN_USERNAME_NOT_EXIST.getCode());
        }
        //在职且状态正常
        if ("Y".equals(baseSysUser.getLockStatus())) {
            return ResponseInfo.error("账号已锁定", LOGIN_ERROR.getCode());
        }
        /*密码验证部分*/
        String salt = baseSysUser.getSalt();
        String basePwd = baseSysUser.getPassword();
        String password = sysUser.getPassword() + salt;
        password = MD5Utils.md5(password);
        log.info(password);
        //判断密码是否正确
        if (BeanUtil.allIsEmpty(password) || !basePwd.equals(password)) {
            return ResponseInfo.error(ResultCodeEnum.LOGIN_PASSWORD_ERROR.getMessage(), ResultCodeEnum.LOGIN_PASSWORD_ERROR.getCode());
        }

        //登录验证密码是否简单
        Integer code = isSimplePassword(sysUser.getPassword()).getCode();
        if (code != 200){
            return ResponseInfo.error(ResultCodeEnum.LOGIN_PASSWORD_IS_SIMPLE.getMessage(), ResultCodeEnum.LOGIN_PASSWORD_IS_SIMPLE.getCode());
        }

        //记录用户登录的时间
        Date currentTime = new Date();
        baseSysUser.setLastLogin(currentTime);
        //记录用户登录的ip
        String ipAddress = getClientIpAddress(servletRequest);
        log.info("登录用户：{}\n登录IP：{}", baseSysUser.getEngName(), ipAddress);
        /*记录是否是内网登录*/
        String netFlag = ipAddressCheck(ipAddress);
        /*更新登录信息*/
        lambdaUpdate().eq(SdUser::getEngName, username)
                .set(SdUser::getLastLogin, currentTime)
                .set(SdUser::getNetFlag, netFlag);
        /*根据角色获取权限ID*/
        LambdaQueryWrapper<SdPrgPrivilege> privilegeWrapper = new LambdaQueryWrapper<SdPrgPrivilege>()
                .eq(SdPrgPrivilege::getPosId, baseSysUser.getPosId());
        List<String> prgId = privilegeMapper.selectList(privilegeWrapper).stream()
                .map(SdPrgPrivilege::getPrgId).collect(Collectors.toList());
        //复制 sysUserVo 信息到 sysUserTokenDto
        SysUserTokenDTO dto = new SysUserTokenDTO();
        BeanUtils.copyProperties(baseSysUser, dto);
        dto.setDeptId(baseSysUser.getDeptNo());
        dto.setUserName(baseSysUser.getChiName());
        dto.setLoginName(baseSysUser.getEngName());
        dto.setRole(baseSysUser.getPosId());
        dto.setPrgIds(prgId);
        dto.setLoginIp(ipAddress);
        // 添加用户权限
        String token = JWTUtil.generateToken(dto);
        String key = baseSysUser.getUserId() + "_" + "token";
        //MS TODO 2024/7/26 : 对TK进行分类存储
        redisService.set(key, token);
        if (LoginPermEnum.permExist(baseSysUser.getUserId())){
            redisService.setOutTime(key, -1);
        }
        LambdaUpdateWrapper<SdUser> userWrapper = new LambdaUpdateWrapper<SdUser>()
                .eq(SdUser::getUserId, baseSysUser.getUserId())
                .set(SdUser::getLastLogin,currentTime);
        sdUserMapper.update(null,userWrapper);
        SdUserDto user2 = sdUserMapper.getUser2(baseSysUser.getUserId(), baseSysUser.getCompanyId());
        if (ZStringUtils.isEmpty(prgId)) throw new ServiceErrorException("用户权限为空、请联系管理员添加");
        HashMap<String, Object> map = new HashMap<>(3);
        /*获取权限信息*/
        map.put("Authorization", key);
        map.put("name", baseSysUser.getChiName());
        map.put("companyId", baseSysUser.getCompanyId());
        map.put("userInfo", user2);
        return ResponseInfo.ok(map);
    }

    private String ipAddressCheck(String ipAddress) {
        InetAddress ia = null;
        try {
            InetAddress ad = InetAddress.getByName(ipAddress);
            byte[] ip = ad.getAddress();
            ia = InetAddress.getByAddress(ip);
        } catch (UnknownHostException e) {
            log.warn("异常接口：login\n异常原因：IP地址解析失败", e);
        }
        if (ia != null && (ia.isSiteLocalAddress() || ia.isLoopbackAddress())) {
            return "N";
        }
        return "Y";
    }

    public static String getClientIpAddress(HttpServletRequest request) {
        String errAddressMsg = "unknown";
        String localHostAddress1 = "127.0.0.1";
        String localHostAddress2 = "0:0:0:0:0:0:0:1";

        String ipAddress = request.getHeader("X-Forwarded-For");
        if (ipAddress == null || ipAddress.isEmpty() || errAddressMsg.equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("Proxy-Client-IP");
        }
        if (ipAddress == null || ipAddress.isEmpty() || errAddressMsg.equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ipAddress == null || ipAddress.isEmpty() || errAddressMsg.equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ipAddress == null || ipAddress.isEmpty() || errAddressMsg.equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (ipAddress == null || ipAddress.isEmpty() || errAddressMsg.equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getRemoteAddr();
            // 如果是localhost访问，可能会返回"0:0:0:0:0:0:0:1"或"127.0.0.1"，这里简化处理
            if (localHostAddress2.equals(ipAddress) || localHostAddress1.equals(ipAddress)) {
                ipAddress = localHostAddress1;
            }
        }

        // 多个代理的情况，第一个IP为客户端真实IP，多个IP按照','分割
        if (ipAddress != null && ipAddress.contains(",")) {
            ipAddress = ipAddress.split(",")[0];
        }

        return ipAddress;
    }

    @Override
    public ResponseInfo progQuery() {
        /*根据权限ID返回展示窗口的集合*/
        boolean ignoreFlag =superName.contains(UserIdThread.get().getUserName());
        log.info("ignoreFlag:{}",ignoreFlag);
        List<String> prgIds = UserIdThread.get().getPrgIds();
        if (!ignoreFlag && ArrayUtils.isEmpyt(prgIds)){
            return ResponseInfo.warn("您的账号暂无任何授权");
        }
        LambdaQueryWrapper<SdProgram> wrapper = new LambdaQueryWrapper<SdProgram>()
                .in(!ignoreFlag,SdProgram::getPrgId, prgIds);
        List<SdProgram> dtos = programMapper.selectList(wrapper);
        return ResponseInfo.ok(dtos);
    }

    @Override
    public Integer getUserIdByName(String userName) {
        return sdUserMapper.getUserIdByName(userName);
    }

    /**
     * 判断 密码过于简单，请重新设置密码
     * 不能密码小于8位
     * 不能全是一样的字符，如11111
     * 不能是连续的数字或者字母，如123456，654321，abcde
     * @param password 要检查的密码
     */
    ResponseInfo isSimplePassword(String password) {
        //1.判断是否是8位数字
        if (password.length() < 8) {
            System.out.println("密码不能小于8位");
            return ResponseInfo.error("密码不能小于8位");
        }

        // 2. 检查是否全是一样的字符
        if (password.chars().distinct().count() == 1) {
            System.out.println("密码不能全是一样的字符");
            return ResponseInfo.error("密码不能全是一样的字符");
        }

        // 3. 检查是否是连续的数字或者字母
        if (isSequential(password.toLowerCase())) { // 转换为小写以忽略大小写差异
            System.out.println("密码不能是连续的数字或字母");
            return ResponseInfo.error("密码不能全是连续的字符");
        }

        return ResponseInfo.ok();
    }

    // 辅助方法：判断字符串是否为连续字符序列（包括数字和字母）
    private static boolean isSequential(String str) {
        for (int i = 0; i < str.length() - 3; i++) {
            int diff1 = str.charAt(i + 1) - str.charAt(i);
            int diff2 = str.charAt(i + 2) - str.charAt(i + 1);
            int diff3 = str.charAt(i + 3) - str.charAt(i + 2);

            // 检查递增或递减连续性
            if ((diff1 == 1 && diff2 == 1 && diff3 == 1) || (diff1 == -1 && diff2 == -1 && diff3 == -1)) {
                return true;
            }
        }
        return false;
    }

}
